<?php
/**
 * Created by PhpStorm.
 * User: ZhuXueSong
 * Date: 2019-07-01
 * Time: 09:46
 */

namespace App\Services\Third;

use App\Constant\ConstantMap;
use App\Exceptions\ExceptionSms;
use App\Models\RequestLog;
use App\Models\UserSms;
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\HttpProfile;

class Sms
{
    private $accessKey;
    private $secret;
    private $srvUrl = 'dysmsapi.aliyuncs.com';

    const MOBILE_CODE_TEMPLATE = 'SMS_287765336'; // 验证码模板
    const TASK_NOTICE_TEMPLATE = 'SMS_463684018'; // 任务预警通知模板

    const SMS_SING_USER   = '驿现';
    const SMS_SIGN_WAITER = '';

    /**
     * Sms constructor.
     * @param $accessKey
     * @param $secret
     */
    public function __construct($accessKey, $secret)
    {
        $this->accessKey = $accessKey; //Config::get('common.sms_key');
        $this->secret    = $secret;    //Config::get('common.sms_secret');
        \Log::info("key = " . $this->accessKey);
        \Log::info("secret = " . $this->secret);
    }

    /**
     * 发送手机验证码
     * @param string $mobile
     * @param string $code
     * @param int $signType 签名类型
     * @return bool|\stdClass
     */
    public function sendMobileCode($mobile, $code, $signType = self::SMS_SING_USER)
    {
        $signName = $signType == ConstantMap::$smsTypeMap ? self::SMS_SING_USER : self::SMS_SIGN_WAITER;
        return $this->sendSms($mobile, $code, self::MOBILE_CODE_TEMPLATE, self::SMS_SING_USER);
    }

    /**
     * 发送手机验证码
     * @param $mobile
     * @param $code
     * @param $template
     * @param string $signName
     * @return bool|\stdClass
     */
    private function sendSms($mobile, $code, $template, $signName)
    {
        $params                  = [
            'PhoneNumbers'  => $mobile,
            'SignName'      => $signName,
            'TemplateCode'  => $template,
            "Action"        => "SendSms",
            "RegionId"      => "cn-hangzhou",
            "Version"       => "2017-05-25",
            'TemplateParam' => ['code' => $code]
        ];
        $params['TemplateParam'] = json_encode($params['TemplateParam'], JSON_UNESCAPED_UNICODE);
        $result                  = $this->request($this->accessKey, $this->secret, $this->srvUrl, $params);
        if (isset($result['Code']) && $result['Code'] == "OK") {
            return true;
        } else {
            RequestLog::log($this->srvUrl, '', $params, intval($result['Code'] ?? ''), $result['Message'] ?? '', $result);
            return $result['Message'] ?? $result;
        }
    }

    /**
     * 发送任务通知
     * @param string $mobile 手机号
     * @param array $params 格式 {"project_name": "项目名称", "order_no": "订单号", "hours": "小时"}
     * @return bool|mixed|\stdClass
     * @name:sendTaskNotice
     * @author: lisa
     * @datetime: 2023-10-27 11:24
     */
    public function sendTaskNotice($mobile, array $params)
    {
        $project_name = $params['project_name'] ?? null;
        $order_no     = $params['order_no'] ?? null;
        $hours        = $params['hours'] ?? null;
        if (!$project_name) ExceptionSms::returnError("任务预警通知：项目名称不能为空");
        if (!$order_no) ExceptionSms::returnError("任务预警通知：订单号不能为空");
        if (!$hours) ExceptionSms::returnError("任务预警通知：小时不能为空");
        $data         = [
            'PhoneNumbers'  => $mobile,
            'SignName'      => self::SMS_SING_USER,
            'TemplateCode'  => self::TASK_NOTICE_TEMPLATE,
            "Action"        => "SendSms",
            "RegionId"      => "cn-hangzhou",
            "Version"       => "2017-05-25",
            'TemplateParam' => json_encode($params, JSON_UNESCAPED_UNICODE)
        ];

        $result = $this->request($this->accessKey, $this->secret, $this->srvUrl, $data);
        if (isset($result['Code']) && $result['Code'] == "OK") {
            return true;
        } else {
            RequestLog::log($this->srvUrl, '', $params, intval($result['Code'] ?? ''), $result['Message'] ?? '', $result);
            return $result['Message'] ?? $result;
        }
    }

    /**
     * 生成签名并发起请求
     * @param $accessKeyId string AccessKeyId (https://ak-console.aliyun.com/)
     * @param $accessKeySecret string AccessKeySecret
     * @param $domain string API接口所在域名
     * @param $params array API具体参数
     * @param $security boolean 使用https
     * @param string $method 使用GET或POST方法请求，VPC仅支持POST
     * @return bool|\stdClass 返回API接口调用结果，当发生错误时返回false
     */
    private function request($accessKeyId, $accessKeySecret, $domain, $params, $security = false, $method = 'POST')
    {
        $apiParams = array_merge([
            "SignatureMethod"  => "HMAC-SHA1",
            "SignatureNonce"   => uniqid(mt_rand(0, 0xffff), true),
            "SignatureVersion" => "1.0",
            "AccessKeyId"      => $accessKeyId,
            "Timestamp"        => gmdate("Y-m-d\TH:i:s\Z"),
            "Format"           => "JSON",
        ], $params);
        ksort($apiParams);
        $sortedQueryStringTmp = "";
        foreach ($apiParams as $key => $value) {
            $sortedQueryStringTmp .= "&" . $this->encode($key) . "=" . $this->encode($value);
        }
        $stringToSign = "${method}&%2F&" . $this->encode(substr($sortedQueryStringTmp, 1));
        $sign         = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
        $signature    = $this->encode($sign);
        $url          = ($security ? 'https' : 'http') . "://{$domain}/";
        try {
            $content = $this->fetchContent($url, $method, "Signature={$signature}{$sortedQueryStringTmp}");
            \Log::info("阿里云发送短信响应信息：" . $content);
            return json_decode($content, true);
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    private function encode($str)
    {
        $res = urlencode($str);
        $res = preg_replace("/\+/", "%20", $res);
        $res = preg_replace("/\*/", "%2A", $res);
        $res = preg_replace("/%7E/", "~", $res);
        return $res;
    }

    private function fetchContent($url, $method, $body)
    {
        $ch = curl_init();
        if ($method == 'POST') {
            curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        } else {
            $url .= '?' . $body;
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            "x-sdk-client" => "php/2.0.0"
        ]);
        if (substr($url, 0, 5) == 'https') {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        $rtn = curl_exec($ch);
        if ($rtn === false) {
            // 大多由设置等原因引起，一般无法保障后续逻辑正常执行，
            // 所以这里触发的是E_USER_ERROR，会终止脚本执行，无法被try...catch捕获，需要用户排查环境、网络等故障
            trigger_error("[CURL_" . curl_errno($ch) . "]: " . curl_error($ch), E_USER_ERROR);
        }
        curl_close($ch);
        return $rtn;
    }
}
